/*
 * Copyright (C) 2014 Freescale Semiconductor, Inc. All Rights Reserved.
 */

/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#define DEBUG

#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/sysdev.h>
#include <linux/bitops.h>
#include <linux/sysfs.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/fsl_devices.h>
#include <mach/hardware.h>
#include <mach/clock.h>
#include <mach/system.h>
#include <mach/dmaengine.h>
#include <mach/dma.h>

#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>

#include <linux/hrtimer.h>
#include <linux/kthread.h>
#include <linux/delay.h>

#define SFTRST (1 << 31)
#define CLKGATE (1 << 30)
#define SOFT_TRIGGER (1 << 27)
#define RUN (1 << 0)
#define PWM_CTRL 0x0
#define PWM_CTRL_SET 0x04
#define PWM_CTRL_CLR 0x08
#define PWM_ACTIVE0 0x10
#define PWM_PERIOD0 0x20
#define PWM_ACTIVE1 0x30
#define PWM_PERIOD1 0x40
#define PWM_ACTIVE2 0x50
#define PWM_PERIOD2 0x60
#define PWM_ACTIVE3 0x70
#define PWM_PERIOD3 0x80
#define PWM_ACTIVE4 0x90
#define PWM_PERIOD4 0xa0
#define PWM_ACTIVE5 0xb0
#define PWM_PERIOD5 0xc0
#define PWM_ACTIVE6 0xd0
#define PWM_PERIOD6 0xe0
#define PWM_ACTIVE7 0xf0
#define PWM_PERIOD7 0x100

#define HSADC_CTRL0 0x0
#define HSADC_CTRL0_SET 0x04
#define HSADC_CTRL0_CLR 0x08
#define HSADC_CTRL1 0x10
#define HSADC_CTRL1_SET 0x14
#define HSADC_CTRL1_CLR 0x18
#define HSADC_CTRL2 0x20
#define HSADC_CTRL2_SET 0x24
#define HSADC_CTRL2_CLR 0x28
#define HSADC_SEQUENCE_SAMPLES_NUM 0x30
#define HSADC_SEQUENCE_NUM 0x40

#define HSADCFRAC 18 /* 18 ~ 35 */
#define HSADCDIV 18  /* OK: 9,18  NG: 36,72 */
#define PLL0_FREQ 480000000UL
#define REF_HSADC_FREQ ((PLL0_FREQ / HSADCFRAC) * 18)
#define HSADC_FREQ (REF_HSADC_FREQ / HSADCDIV)

#define HSADC_DEVICE_NAME "mxs-hsadc" /* hsadc device name */

#define DMA_BUF_SIZE 0x10000 // 一个DMA最大的容量
#define DMA_BLUCK_COUNT 10   // 驱动部分最多缓存多少组数据，保证采样过程不会中断

#define HSADC_DEBUG 0

enum hsadc_adc_percision
{
    HSADC_SAMPLE_PERCISION_8_BIT = 0,
    HSADC_SAMPLE_PERCISION_10_BIT = 0x20000,
    HSADC_SAMPLE_PERCISION_12_BIT = 0x40000,
    HSADC_SAMPLE_PERCISION_MASK = 0x60000
};

static int adc_sample_percision = 12;
module_param(adc_sample_percision, int, S_IRUGO);

struct mxs_hsadc_bluk
{
    struct mxs_dma_desc *desc;
    int is_finish;
};
struct mxs_hsadc_data
{
    struct device *dev;
    void __iomem *hsadc_base;
    void __iomem *pwm_base;

    // Fix 内核连续采样并缓存
    struct work_struct work;
    struct mxs_hsadc_bluk *bluk;
    int bluk_count;
    // end Fix

    int dev_irq;
    int dma_irq;
    int dma_ch;
    struct clk *ref_hsadc_clk;
    struct clk *hsadc_clk;
    struct clk *pwm_clk;
    struct mxs_dma_desc *desc;

    // struct mxs_dma_desc *desc1;
    struct mxs_dma_desc *desc2;
    // struct mxs_dma_desc *desc3;
    // struct mxs_dma_desc *desc4;

    int is_finish1;
    int is_finish2;
    int is_finish3;
    int is_finish4;
    void *buf;
    void *buf2;
    dma_addr_t buf_phy;
    dma_addr_t buf_phy2;
    unsigned int tx_cnt;

    /* char device and file operations interface */
    struct cdev cdev_inst;    /* cdev instance */
    struct class *cdev_class; /* hsadc device class for sysfs */
    int cdev_major;           /* hsadc major number */
    int cdev_index;           /* hsadc index number */

    struct semaphore sem;     /* access lock */
    wait_queue_head_t r_wait; /* wait queue for reading */

    unsigned long total_len;   // total read length
    unsigned long remaint_len; // remaint length to read
    unsigned long seq_len;
    char *cur; // current position

    struct completion thread_run;
    struct completion dma_finish;
    struct task_struct *thread;
    struct platform_device *pdev;
};

static irqreturn_t hsadc_dma_isr(int irq, void *p);
static irqreturn_t hsadc_isr(int irq, void *p);
void hsadc_cleanup_cdev(struct mxs_hsadc_data *pdx);
int init_hsadc_hw(struct mxs_hsadc_data *pd);

int submit_request(struct mxs_hsadc_data *pdx, unsigned long count)
{
    unsigned long sample_count;
    // memset(pdx->buf, 16, count);

    pdx->desc->cmd.cmd.bits.bytes = count;
    pdx->desc->cmd.cmd.bits.pio_words = 0;
    pdx->desc->cmd.cmd.bits.wait4end = 1;
    pdx->desc->cmd.cmd.bits.dec_sem = 1;
    pdx->desc->cmd.cmd.bits.irq = 1;
    pdx->desc->cmd.cmd.bits.command = DMA_WRITE;
    pdx->desc->cmd.address = pdx->buf_phy;
    // printk("request\r\n");
    mxs_dma_reset(pdx->dma_ch);
    if (mxs_dma_desc_append(pdx->dma_ch, pdx->desc))
    {
        printk("fail\r\n");
        return -EINVAL;
    }
    // printk("success\r\n");

    //
    // byte(s) to sample count
    //
    sample_count =
        adc_sample_percision == 8 ? count : (count >> 1); // 10-bit & 12-bit mode a sample word is two bytes size

    writel(sample_count, pdx->hsadc_base + HSADC_SEQUENCE_SAMPLES_NUM);
    writel(1, pdx->hsadc_base + HSADC_SEQUENCE_NUM);

    writel(1 << 31 | 1 << 30 | 1 << 29, pdx->hsadc_base + HSADC_CTRL1); // enable irq

    // mxs_dma_reset(pdx->dma_ch);
    mxs_dma_ack_irq(pdx->dma_ch);
    mxs_dma_enable_irq(pdx->dma_ch, 1);
    if (mxs_dma_enable(pdx->dma_ch))
    {
        return -EINVAL;
    }

    writel(RUN, pdx->hsadc_base + HSADC_CTRL0_SET);
    writel(SOFT_TRIGGER, pdx->hsadc_base + HSADC_CTRL0_SET);

    return 0;
}

int submit_request2(struct mxs_hsadc_data *pdx, unsigned long count)
{
    unsigned long sample_count;
    // memset(pdx->buf2, 0, count);

    pdx->desc->cmd.cmd.bits.bytes = count;
    pdx->desc->cmd.cmd.bits.pio_words = 0;
    pdx->desc->cmd.cmd.bits.wait4end = 1;
    pdx->desc->cmd.cmd.bits.dec_sem = 1;
    pdx->desc->cmd.cmd.bits.irq = 1;
    pdx->desc->cmd.cmd.bits.command = DMA_WRITE;
    pdx->desc->cmd.address = pdx->buf_phy2;
    // printk("request\r\n");
    if (mxs_dma_desc_append(pdx->dma_ch, pdx->desc))
    {
        printk("fail\r\n");
        return -EINVAL;
    }
    // printk("success\r\n");

    //
    // byte(s) to sample count
    //
    sample_count =
        adc_sample_percision == 8 ? count : (count >> 1); // 10-bit & 12-bit mode a sample word is two bytes size

    writel(sample_count, pdx->hsadc_base + HSADC_SEQUENCE_SAMPLES_NUM);
    writel(1, pdx->hsadc_base + HSADC_SEQUENCE_NUM);

    writel(1 << 31 | 1 << 30 | 1 << 29, pdx->hsadc_base + HSADC_CTRL1); // enable irq

    mxs_dma_reset(pdx->dma_ch);
    mxs_dma_ack_irq(pdx->dma_ch);
    mxs_dma_enable_irq(pdx->dma_ch, 1);
    if (mxs_dma_enable(pdx->dma_ch))
    {
        return -EINVAL;
    }

    writel(RUN, pdx->hsadc_base + HSADC_CTRL0_SET);
    writel(SOFT_TRIGGER, pdx->hsadc_base + HSADC_CTRL0_SET);

    return 0;
}

#if 0
ssize_t hsadc_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{
	struct mxs_hsadc_data *pdx = (struct mxs_hsadc_data*)filp->private_data;
	int ret = 0;
	int timeout;
	
    
	DECLARE_WAITQUEUE(wait, current);

#if HSADC_DEBUG
	printk(KERN_INFO "%s> %s start.\n", HSADC_DEVICE_NAME, __FUNCTION__);
#endif

	/* someone has request zero byte data! */
	if(!count)
		return ret;
	// down(&pdx->sem);
	/* push the request into the wait queue, it will be wakeup by the ISR */
	add_wait_queue(&pdx->r_wait, &wait);

	if(filp->f_flags & O_NONBLOCK)
	{
		ret = -EAGAIN;
#if HSADC_DEBUG
		printk(KERN_INFO "%s> file open with O_NONBLOCK.\n", HSADC_DEVICE_NAME);
#endif
		goto acc_out;
	}

	pdx->cur = buf;
	pdx->total_len = 
	pdx->remaint_len = 
		adc_sample_percision == 8 ? count : count &(~0x01);// in 10-bit & 12-bit mode the sample data word size is two bytes

	if(pdx->total_len <= DMA_BUF_SIZE)
		pdx->seq_len = count;
	else
		pdx->seq_len = DMA_BUF_SIZE;

	// init a read and wait for interrupt
	if(submit_request(pdx, pdx->seq_len))
	{
		goto acc_abort;
	}

	while(pdx->remaint_len)
	{
		/* set current process state into sleep */
		__set_current_state(TASK_INTERRUPTIBLE);

#if HSADC_DEBUG
		printk(KERN_INFO "%s> suspend.\n", HSADC_DEVICE_NAME);
#endif
		/* schedule other process to run */
		// up(&pdx->sem);
		// schedule();
#if 0
		/* 2秒仅仅用于调试 */
		int timeout = schedule_timeout(2*HZ);
#else
		/* 采样速率3M/s，DMA只能一次传输65536个点，65536需要21ms */
		/* timeout只要远大于21ms即可 */
		int timeout = schedule_timeout(0.1*HZ);
#endif
#if HSADC_DEBUG
		printk(KERN_INFO "%s> wakeup.\n", HSADC_DEVICE_NAME);
#endif
        
		if (timeout == 0) {  // 因为超时
            ret = -EIO;
			goto acc_out;
		}
		/* wakeup point */
		if(signal_pending(current))
		{
			/* no data, we are wakeuped by the signal, do nothing */
			ret = -ERESTARTSYS;
			goto acc_out;
		}

		if(copy_to_user(pdx->cur, pdx->buf, pdx->seq_len))
		{
			ret = pdx->total_len - pdx->remaint_len;
			goto acc_abort;
		}
		else
		{
			// update position
			pdx->cur += pdx->seq_len;
			pdx->remaint_len -= pdx->seq_len;

			if(pdx->remaint_len > DMA_BUF_SIZE)
			{
				 pdx->seq_len = DMA_BUF_SIZE;
			}
			else
			{
				 // this is the last piease of data to read
				 pdx->seq_len = pdx->remaint_len;
			}
	
			if(pdx->seq_len) {
				submit_request(pdx, pdx->seq_len);		
			}
		}
		down(&pdx->sem);
#if HSADC_DEBUG
		printk(KERN_INFO "%s> remaint = 0x%lx\n",
			HSADC_DEVICE_NAME, pdx->remaint_len);
#endif
	}

	ret = pdx->total_len - pdx->remaint_len;

acc_abort:
	;// up(&pdx->sem);
acc_out:
	remove_wait_queue(&pdx->r_wait, &wait);
	__set_current_state(TASK_RUNNING);

#if HSADC_DEBUG
	printk(KERN_INFO "%s> %s end.\n", HSADC_DEVICE_NAME, __FUNCTION__);
#endif
	
	return ret;
}
#else
ssize_t hsadc_read3(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{
    struct mxs_hsadc_data *pdx = (struct mxs_hsadc_data *)filp->private_data;
    pdx->seq_len = 60000;
    printk("111\r\n");

    pdx->cur = buf;
    pdx->total_len = 1000;
    pdx->remaint_len =
        adc_sample_percision == 8 ? count : count & (~0x01); // in 10-bit & 12-bit mode the sample data word size is two bytes

    if (pdx->total_len <= DMA_BUF_SIZE)
        pdx->seq_len = count;
    else
        pdx->seq_len = DMA_BUF_SIZE;
    // pdx->desc = pdx->desc1;
    // if(submit_request(pdx, pdx->seq_len)) {
    //     printk("fail\r\n");
    // }
    return 2;
}

// count 不能大于1000（也就是500个数据点）
ssize_t hsadc_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{
    struct mxs_hsadc_data *pdx = (struct mxs_hsadc_data *)filp->private_data;
    int cpy_len = 0;
    short *pbuf, *tbuf;
    int i,j;
    // printk("return 0\r\n");
    // return 0;
    // return -EAGAIN;

    if (count > 1000)
    {
        count = 1000;
    }
    
    cpy_len = count/2;
    

    if (pdx->is_finish1 == 1)
    {
        pbuf = pdx->buf;
        tbuf = buf;
        // pbuf[0] = 0x1816;
        // copy_to_user(buf, pdx->buf, 4);
        // tbuf[0] = pbuf[0];
        // tbuf[1] = pbuf[1];
        
        /* bug 不知道怎么设置DMA半字节传输
        */
        for (j = 0, i = 0;i < 60000/2 && j < cpy_len; i+=120) {
            tbuf[j++] = pbuf[i];
            // tbuf[j++] = 0x120;
        }
        pdx->is_finish1 = 0;
        // printk("a\r\n");
    }
    else if (pdx->is_finish2 == 1)
    {
        pbuf = pdx->buf2;
        tbuf = buf;
        // pbuf[0] = 0x4440;
        // copy_to_user(buf, pdx->buf2, cpy_len);
        for (j = 0, i = 0;i < 60000/2 && j < cpy_len; i+=120) {
            tbuf[j++] = pbuf[i];
            // tbuf[j++] = 0x120;
        }

        

        
        // pbuf[0] = 0x3936;
        
        // tbuf[0] = pbuf[0];
        // tbuf[1] = pbuf[1];
        pdx->is_finish2 = 0;
        // printk("b\r\n");
    }
    else {
        printk("em\r\n");
        cpy_len = 0;
        j = 0;
        // pbuf = pdx->buf2;
        // pbuf[0] = 1234;
        // copy_to_user(buf, pdx->buf2, cpy_len);
        return -EAGAIN;
    }
    // return (cpy_len << 1);
    return j * 2;
}
#endif

int times = 0;
static void sampling_work(struct work_struct *work)
{
    struct mxs_hsadc_data *pdx = container_of(work, struct mxs_hsadc_data, work);

    if (times++ % 5 == 0)
    {
        pdx->seq_len = 0;
    }
    else
    {
        pdx->seq_len = 1000;
    }
    if (submit_request(pdx, pdx->seq_len))
    {
        printk("fail\r\n");
    }
    printk("finish\r\n");
}

int hsadc_ioctl(struct inode *inodep, struct file *filp, unsigned int cmd, unsigned long arg)
{
    struct mxs_hsadc_data *pdx = (struct mxs_hsadc_data *)filp->private_data;
    unsigned long val = 0;

    pdx = NULL;
    val = -1;

#if HSADC_DEBUG
    printk(KERN_INFO "%s> %s.\n", HSADC_DEVICE_NAME, __FUNCTION__);
#endif

    return 0;
}

int hsadc_open(struct inode *inodep, struct file *filp)
{
    struct mxs_hsadc_data *pdx = container_of(inodep->i_cdev, struct mxs_hsadc_data, cdev_inst);

    /* lock the device, make the process open the hsadc sequencely */
    // down(&pdx->sem);

    /* save device instance data in file.private_data */
    filp->private_data = pdx; /* pd is dynamic created in hsadc_probe() */

    // init_hsadc_hw(pdx);

#if HSADC_DEBUG
    printk(KERN_INFO "%s> %s.\n", HSADC_DEVICE_NAME, __FUNCTION__);
#endif

    // mxs_dma_reset(pdx->dma_ch);
    // mxs_dma_ack_irq(pdx->dma_ch);
    // mxs_dma_enable_irq(pdx->dma_ch, 1);
    // up(&pdx->sem);
    return 0;
}

int hsadc_close(struct inode *inodep, struct file *filp)
{
    struct mxs_hsadc_data *pd = (struct mxs_hsadc_data *)filp->private_data;

    // filp->private_data = NULL;

    /* unlock the device,  process can open hsadc */
    // up(&pdx->sem);

#if HSADC_DEBUG
    printk(KERN_INFO "%s> %s.\n", HSADC_DEVICE_NAME, __FUNCTION__);
#endif

    return 0;
}

static const struct file_operations hsadc_fops = {
    .owner = THIS_MODULE,
    .read = hsadc_read,
    .ioctl = hsadc_ioctl,
    .open = hsadc_open,
    .release = hsadc_close,
};

/* mxs-hsadc init driver interface */
int hsadc_init_cdev(struct mxs_hsadc_data *pdx)
{
    int res = 0;
    dev_t devno = 0;

    /* allocate chrdev region dynamical */
    res = alloc_chrdev_region(&devno, 0, 1, HSADC_DEVICE_NAME);

    if (res < 0)
        return res;

    /* init the semaphore as a mutex */
    sema_init(&pdx->sem, 1); /* @== init_MUTEX(&gdevp->sem) */

    init_waitqueue_head(&pdx->r_wait);

    pdx->cdev_major = MAJOR(devno);
    pdx->cdev_index = 0;
    pdx->cdev_inst.owner = THIS_MODULE;

    /* populate sysfs device entry(diractory) */
    pdx->cdev_class = class_create(THIS_MODULE, HSADC_DEVICE_NAME);

    /* init the cdev kernel object, set the fops for user space interface */
    cdev_init(&pdx->cdev_inst, &hsadc_fops);

    res = cdev_add(&pdx->cdev_inst, MKDEV(pdx->cdev_major, pdx->cdev_index), 1);
    if (res)
    {
#if HSADC_DEBUG
        printk(KERN_NOTICE "%s> error %d adding hsadc-%d.\n", HSADC_DEVICE_NAME, res, 0);
#endif
        goto fail_cdev_add;
    }

    /* send uevents to udev，the udevd dynamic create device node(inode) in /dev */
    device_create(pdx->cdev_class, NULL, MKDEV(pdx->cdev_major, pdx->cdev_index), NULL, "%s%d", HSADC_DEVICE_NAME, 0);

#if HSADC_DEBUG
    printk(KERN_INFO "%s> initialized.\n", HSADC_DEVICE_NAME);
#endif

    return 0;

fail_cdev_add:
    class_destroy(pdx->cdev_class);

    //fail_malloc:
    unregister_chrdev_region(devno, 1);
    return res;
}

/* mxs-hsadc driver unload routine */
void hsadc_cleanup_cdev(struct mxs_hsadc_data *pdx)
{
    device_destroy(pdx->cdev_class, MKDEV(pdx->cdev_major, pdx->cdev_index));
    cdev_del(&pdx->cdev_inst); /* against to cdev_add() */
    class_destroy(pdx->cdev_class);
    unregister_chrdev_region(MKDEV(pdx->cdev_major, pdx->cdev_index), 1); /* release the region */

#if HSADC_DEBUG
    printk(KERN_INFO "%s> %s.\n", HSADC_DEVICE_NAME, __FUNCTION__);
#endif

    return;
}

//===============================================================================================================================

static irqreturn_t hsadc_dma_isr(int irq, void *p)
{
    struct mxs_hsadc_data *pd = (struct mxs_hsadc_data *)p;
    mxs_dma_ack_irq(pd->dma_ch);
    mxs_dma_cooked(pd->dma_ch, NULL);
    // printk("irq dma\r\n");
    //dev_dbg(pd->dev, "dma\n");
    // wake_up_interruptible(&pd->r_wait);

    // complete(&pd->thread_run);
    complete(&pd->dma_finish);
    // schedule_work(&pd->work);// 这个DMA中断里不能用work，否则会导致只有一个DMA irq被响应
    return IRQ_HANDLED;
}

static irqreturn_t hsadc_isr(int irq, void *p)
{
    struct mxs_hsadc_data *pd = (struct mxs_hsadc_data *)p;
    // printk("irq adc\r\n");
#if HSADC_DEBUG
    u32 interrupt = readl(pd->hsadc_base + HSADC_CTRL1);
    writel(1 << 27, pd->hsadc_base + HSADC_CTRL1_SET);
    writel(1 << 26, pd->hsadc_base + HSADC_CTRL1_SET);

    dev_dbg(pd->dev, "irq %08x %s %s %s %s %s \n", interrupt,
            interrupt & (1 << 5) ? "FIFO_EMPTY" : "",
            interrupt & (1 << 4) ? "END_ONE_SEQ" : "",
            interrupt & (1 << 3) ? "ADC_DONE" : "",
            interrupt & (1 << 2) ? "FIFO_OVERFLOW" : "",
            interrupt & (1 << 1) ? "TIMEOUT" : "");
#else
    writel(3 << 26, pd->hsadc_base + HSADC_CTRL1_SET);
#endif

    return IRQ_HANDLED;
}

void init_sample_percision(struct mxs_hsadc_data *pdx, int percision)
{
    unsigned long val = 0;

    val = readl(pdx->hsadc_base + HSADC_CTRL0);
    val &= ~(HSADC_SAMPLE_PERCISION_MASK);

    switch (percision)
    {
    case 8:
        val |= HSADC_SAMPLE_PERCISION_8_BIT;
        break;
    case 10:
        val |= HSADC_SAMPLE_PERCISION_10_BIT;
        break;
    case 12:
        val |= HSADC_SAMPLE_PERCISION_12_BIT;
        break;
    default:
        val |= HSADC_SAMPLE_PERCISION_8_BIT;
        break;
    }

    writel(val, pdx->hsadc_base + HSADC_CTRL0);

    // precharge
    writel(1, pdx->hsadc_base + HSADC_CTRL2_SET);

#if HSADC_DEBUG
    printk(KERN_INFO "%s> hsadc sample data is set to %d-bit mode.\n", HSADC_DEVICE_NAME, sample_percis);
#endif

    return;
}

#if 1

int init_hsadc_hw(struct mxs_hsadc_data *pd)
{
	//u32 ctrl0 = 0;
#if 0
	/* reset pwm block */
	writel(SFTRST, pd->pwm_base + PWM_CTRL_SET);
	while (!(readl(pd->pwm_base + PWM_CTRL) & CLKGATE));
	writel(SFTRST, pd->pwm_base + PWM_CTRL_CLR);
	writel(CLKGATE, pd->pwm_base + PWM_CTRL_CLR);
	while ((readl(pd->pwm_base + PWM_CTRL) & CLKGATE));
#endif
	writel(3, pd->pwm_base + PWM_CTRL_CLR); //disable PWM channel 0 1

	writel(0|(1<<16), pd->pwm_base + PWM_ACTIVE0);
	/*  PERIOD Register
		26 25 24 23 22-20 19-18 17-16
		0  1  0  0  000   10    11 */
	writel( (0x020B<<16) | 999, pd->pwm_base + PWM_PERIOD0);

	writel(0|((999/2)<<16), pd->pwm_base + PWM_ACTIVE1);
	/*  PERIOD Register
		26 25 24 23 22-20 19-18 17-16
		0  1  0  0  000   10    11 */
	writel( (0x020B<<16) | 999, pd->pwm_base + PWM_PERIOD1);

	writel(3, pd->pwm_base + PWM_CTRL_SET); //enable PWM channel 0 1

	/* Workaround for ENGR116296
		HSADC: Soft reset causes unexpected request to DMA */
	writel(SFTRST, pd->hsadc_base + HSADC_CTRL0_CLR);

	writel((readl(pd->hsadc_base + HSADC_CTRL0) | SFTRST) & (~CLKGATE),\
		pd->hsadc_base + HSADC_CTRL0);
	writel(CLKGATE, pd->hsadc_base + HSADC_CTRL0_SET);
	writel(CLKGATE, pd->hsadc_base + HSADC_CTRL0_CLR);
	writel(CLKGATE, pd->hsadc_base + HSADC_CTRL0_SET);

	/* reset hsadc block */
	writel(SFTRST, pd->hsadc_base + HSADC_CTRL0_CLR);
	writel(CLKGATE, pd->hsadc_base + HSADC_CTRL0_CLR);
	writel(SFTRST, pd->hsadc_base + HSADC_CTRL0_SET);
	while (!(readl(pd->hsadc_base + HSADC_CTRL0) & CLKGATE));
	writel(SFTRST, pd->hsadc_base + HSADC_CTRL0_CLR);
	writel(CLKGATE, pd->hsadc_base + HSADC_CTRL0_CLR);

	writel(1<<13, pd->hsadc_base + HSADC_CTRL2_CLR); //clear power down
	writel(0x7<<1, pd->hsadc_base + HSADC_CTRL2_SET); //input pin is HSADC0

	//ctrl0 = readl(pd->hsadc_base + HSADC_CTRL0);
	//ctrl0 &= ~(0x381FF03F); //triggered by software, 8-bits sample
	//writel(ctrl0, pd->hsadc_base + HSADC_CTRL0);
	init_sample_percision(pd, adc_sample_percision);

	writel(1, pd->hsadc_base + HSADC_CTRL2_SET); //assert adc precharge

	return 0;
}
#else  // 自己写的初始化不能用，因为它关闭了所有PWM，导致LCD没有显示，这个问题以后再解决
int init_hsadc_hw(struct mxs_hsadc_data *pd)
{
    //u32 ctrl0 = 0;
#if 0
	/* reset pwm block */
	writel(SFTRST, pd->pwm_base + PWM_CTRL_SET);
	while (!(readl(pd->pwm_base + PWM_CTRL) & CLKGATE));
	writel(SFTRST, pd->pwm_base + PWM_CTRL_CLR);
	writel(CLKGATE, pd->pwm_base + PWM_CTRL_CLR);
	while ((readl(pd->pwm_base + PWM_CTRL) & CLKGATE));
#endif
    writel(3, pd->pwm_base + PWM_CTRL_CLR); //disable PWM channel 0 1

#if 0
    writel(0 | (1 << 16), pd->pwm_base + PWM_ACTIVE0);
    /*  PERIOD Register
		26 25 24 23 22-20 19-18 17-16
		0  1  0  0  000   10    11 */
    writel((0x020B << 16) | 999, pd->pwm_base + PWM_PERIOD0);

    writel(0 | ((999 / 2) << 16), pd->pwm_base + PWM_ACTIVE1);
    /*  PERIOD Register
		26 25 24 23 22-20 19-18 17-16
		0  1  0  0  000   10    11 */
    writel((0x020B << 16) | 999, pd->pwm_base + PWM_PERIOD1);
    writel(3, pd->pwm_base + PWM_CTRL_SET); //enable PWM channel 0 1
# else // debug wu meng long
    writel(0x7f, pd->pwm_base + PWM_CTRL_CLR); //disable PWM channel 0 1
    writel(0 | (0x1000 << 16), pd->pwm_base + PWM_ACTIVE0);
    /*  PERIOD Register
		26 25 24 23 22-20 19-18 17-16
		0  1  0  0  000   10    11 */
    writel((0x022B << 16) | 999, pd->pwm_base + PWM_PERIOD0);
    writel(
        (0x1 << 26) |
        (0x1 << 25) |
        (0x0 << 20) | /*div 8*/
        (0x2 << 18) |
        (0x2 << 16)  |
        0x2000, 
        pd->pwm_base + PWM_PERIOD0);

    // writel(0 | ((999 / 2) << 16), pd->pwm_base + PWM_ACTIVE1);
    // /*  PERIOD Register
	// 	26 25 24 23 22-20 19-18 17-16
	// 	0  1  0  0  000   10    11 */
    // writel((0x022B << 16) | 999, pd->pwm_base + PWM_PERIOD1);
    // writel(0, pd->pwm_base + PWM_CTRL_SET); //enable PWM channel 0 1
#endif    

    

    /* Workaround for ENGR116296
		HSADC: Soft reset causes unexpected request to DMA */
    writel(SFTRST, pd->hsadc_base + HSADC_CTRL0_CLR);

    writel((readl(pd->hsadc_base + HSADC_CTRL0) | SFTRST) & (~CLKGATE),
           pd->hsadc_base + HSADC_CTRL0);
    writel(CLKGATE, pd->hsadc_base + HSADC_CTRL0_SET);
    writel(CLKGATE, pd->hsadc_base + HSADC_CTRL0_CLR);
    writel(CLKGATE, pd->hsadc_base + HSADC_CTRL0_SET);

    /* reset hsadc block */
    writel(SFTRST, pd->hsadc_base + HSADC_CTRL0_CLR);
    writel(CLKGATE, pd->hsadc_base + HSADC_CTRL0_CLR);
    writel(SFTRST, pd->hsadc_base + HSADC_CTRL0_SET);
    while (!(readl(pd->hsadc_base + HSADC_CTRL0) & CLKGATE))
        ;
    writel(SFTRST, pd->hsadc_base + HSADC_CTRL0_CLR);
    writel(CLKGATE, pd->hsadc_base + HSADC_CTRL0_CLR);

    writel(1 << 13, pd->hsadc_base + HSADC_CTRL2_CLR);  //clear power down
    writel(0x7 << 1, pd->hsadc_base + HSADC_CTRL2_SET); //input pin is HSADC0
    
    

    
    //ctrl0 = readl(pd->hsadc_base + HSADC_CTRL0);
    //ctrl0 &= ~(0x381FF03F); //triggered by software, 8-bits sample
    //writel(ctrl0, pd->hsadc_base + HSADC_CTRL0);
    init_sample_percision(pd, adc_sample_percision);

    writel(1, pd->hsadc_base + HSADC_CTRL2_SET); //assert adc precharge

    int ret;

    
    writel(0x3 << 28, pd->hsadc_base + HSADC_CTRL0_CLR); //  triger by soft
    // writel(0x1 << 28, pd->hsadc_base + HSADC_CTRL0_SET);  // triger by pwm

    ret = readl(pd->hsadc_base + HSADC_CTRL0);
    printk("ret %x\r\n", ret);
    ret = readl(pd->pwm_base + PWM_PERIOD0);
    printk("PWM_PERIOD ret %x b26 [%x]\r\n", ret, ret & (1 << 26));
    ret = readl(pd->pwm_base + PWM_PERIOD1);
    printk("PWM_PERIOD ret %x b26 [%x]\r\n", ret, ret & (1 << 26));
    ret = readl(pd->pwm_base + PWM_PERIOD2);
    printk("PWM_PERIOD ret %x b26 [%x]\r\n", ret, ret & (1 << 26));
    ret = readl(pd->pwm_base + PWM_PERIOD3);
    printk("PWM_PERIOD ret %x b26 [%x]\r\n", ret, ret & (1 << 26));
    ret = readl(pd->pwm_base + PWM_PERIOD4);
    printk("PWM_PERIOD ret %x b26 [%x]\r\n", ret, ret & (1 << 26));
    ret = readl(pd->pwm_base + PWM_PERIOD5);
    printk("PWM_PERIOD ret %x b26 [%x]\r\n", ret, ret & (1 << 26));
    ret = readl(pd->pwm_base + PWM_PERIOD6);
    printk("PWM_PERIOD ret %x b26 [%x]\r\n", ret, ret & (1 << 26));
    ret = readl(pd->pwm_base + PWM_PERIOD7);
    printk("PWM_PERIOD ret %x b26 [%x]\r\n", ret, ret & (1 << 26));
    
    return 0;
}
#endif

/*
submit_request(pdx, 60000);
submit_request2(pdx, 60000);
循环20次（2.4MB)，测试效果，约耗时0.77秒，速率得3.11MB
与指标3.2MB相近

[  606.330000] thread finish one frame
[  607.110000] thread finish one frame
[  607.880000] thread finish one frame
[  608.650000] thread finish one frame
[  609.430000] thread finish one frame
[  610.200000] thread finish one frame
[  610.970000] thread finish one frame
*/
int thread_hsadc(void *arg)
{
    struct mxs_hsadc_data *pdx = (struct mxs_hsadc_data *)arg;
    int times = 0;
    short *pbuf;
    // 依旧打开失败，需要先加载mxs-hsadc2.ko然后adc-t.elf
    init_hsadc_hw(pdx);
    printk("thread run\r\n");
    while (!kthread_should_stop())
    {
        // msleep(1000);
        // 等待 complete 被调用，
        // printk("start\r\n");
        pdx->desc->cmd.address = pdx->buf_phy;
        submit_request(pdx, 60000);
        
        if (wait_for_completion_timeout(&pdx->dma_finish, msecs_to_jiffies(1000)))
        {
            // touch_report(s7611->client_shadow, NULL);
            // printk("thread print success1\r\n");
            pdx->is_finish1 = 1;
            pbuf = pdx->buf;
            // pbuf[0] = 0x1111;
        }
        else
        {
            // times = 0;
            printk("dma fail1\r\n");
            // init_hsadc_hw(pdx);
        }
        // if (times++ >= 0)
        // {
        //     times = 0;
        //     pbuf = pdx->buf;
            
        //     // printk("one \r\n");
        //     // printk("%x %x %x %x| ", pbuf[0], pbuf[1], pbuf[2], pbuf[3]);
        //     pbuf = pdx->buf2;
        //     // printk("%x %x %x %x \r\n ", pbuf[0], pbuf[1], pbuf[2], pbuf[3]);
        // }


        // pdx->desc->cmd.address = pdx->buf_phy2;
        submit_request2(pdx, 60000);
        if (wait_for_completion_timeout(&pdx->dma_finish, msecs_to_jiffies(1000))){
            // printk("thread print success2\r\n");
            pdx->is_finish2 = 1;
        }
        else
        {
            // times = 0;
            printk("dma fail2\r\n");
        }

        if (times++ >= 20)
        {
            times = 0;
            // printk("thread finish one frame\r\n");
            pbuf = pdx->buf;
            int i, j;
            for (i = 29990;i < 30100; i+=1) {
                // tbuf[j++] = ;
                // printk("%d %x\r\n", i, *(pbuf + i));
            }
            
            // printk("two\r\n");
            // printk("%x %x %x %x| ", pbuf[0], pbuf[1], pbuf[2], pbuf[3]);
            pbuf = pdx->buf2;
            // printk("%x %x %x %x \r\n ", pbuf[0], pbuf[1], pbuf[2], pbuf[3]);
        }
    }

    complete(&pdx->thread_run);
}

static int __devinit mxs_hsadc_probe(struct platform_device *pdev)
{
    struct mxs_hsadc_data *pd;
    struct resource *res;
    int rlevel = 0;

    pd = kzalloc(sizeof(*pd), GFP_KERNEL);
    if (pd)
        rlevel++;
    else
        goto quit;

    INIT_WORK(&pd->work, sampling_work);

    pd->dev = &pdev->dev;
    platform_set_drvdata(pdev, pd);
    pd->pdev = pdev;
    rlevel++;

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (res == NULL)
        goto quit;
    pd->hsadc_base = ioremap(res->start, res->end - res->start);
    if (pd->hsadc_base)
        rlevel++;
    else
        goto quit;

    res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
    if (res == NULL)
        goto quit;
    pd->pwm_base = ioremap(res->start, res->end - res->start);
    if (pd->pwm_base)
        rlevel++;
    else
        goto quit;

    res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
    if (res)
        pd->dev_irq = res->start;
    else
        goto quit;

    res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
    if (res)
        pd->dma_irq = res->start;
    else
        goto quit;

    res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
    if (res)
        pd->dma_ch = res->start;
    else
        goto quit;

    pd->ref_hsadc_clk = clk_get(NULL, "ref_hsadc");
    if (pd->ref_hsadc_clk)
        rlevel++;
    else
        goto quit;

    pd->hsadc_clk = clk_get(NULL, "hsadc");
    if (pd->hsadc_clk)
        rlevel++;
    else
        goto quit;

    pd->pwm_clk = clk_get(NULL, "pwm");
    if (pd->pwm_clk)
        rlevel++;
    else
        goto quit;

    clk_enable(pd->ref_hsadc_clk);
    clk_enable(pd->hsadc_clk);
    clk_enable(pd->pwm_clk);
    rlevel++;

    clk_set_rate(pd->ref_hsadc_clk, REF_HSADC_FREQ);
    clk_set_rate(pd->hsadc_clk, HSADC_FREQ);

    if (request_irq(pd->dma_irq, hsadc_dma_isr, 0, "hsadc dma", pd))
    {
        printk("dma fail\r\n");
        goto quit;
    }
    else
    {
        rlevel++;
        printk("dma auccess\r\n");
    }

    if (request_irq(pd->dev_irq, hsadc_isr, 0, "hsadc irq", pd))
        goto quit;
    else
        rlevel++;

    if (mxs_dma_request(pd->dma_ch, pd->dev, "hsadc"))
        goto quit;
    else
        rlevel++;

    mxs_dma_disable(pd->dma_ch);

    pd->desc = mxs_dma_alloc_desc();
    if (pd->desc == NULL)
        goto quit;

    // pd->desc1 = mxs_dma_alloc_desc();
    // if (pd->desc1==NULL)
    // 	goto quit;
    pd->desc2 = mxs_dma_alloc_desc();
    if (pd->desc2==NULL)
    	goto quit;
    // pd->desc3 = mxs_dma_alloc_desc();
    // if (pd->desc3==NULL)
    // 	goto quit;
    // pd->desc4 = mxs_dma_alloc_desc();
    // if (pd->desc4==NULL)
    // 	goto quit;
    pd->is_finish1 = 1;

    memset(&pd->desc->cmd, 0, sizeof(pd->desc->cmd));
    memset(&pd->desc2->cmd, 0, sizeof(pd->desc2->cmd));
    rlevel++;

    pd->buf = dma_alloc_coherent(NULL, DMA_BUF_SIZE, &pd->buf_phy, GFP_KERNEL);
    if (!pd->buf)
        goto quit;

    // pd->buf1 = dma_alloc_coherent(NULL, DMA_BUF_SIZE, &pd->buf_phy1, GFP_KERNEL);
    // if(!pd->buf1)
    // 	goto quit;
    pd->buf2 = dma_alloc_coherent(NULL, DMA_BUF_SIZE, &pd->buf_phy2, GFP_KERNEL);
    if (!pd->buf2)
        goto quit;
    // pd->buf = dma_alloc_coherent(NULL, DMA_BUF_SIZE, &pd->buf_phy, GFP_KERNEL);
    // if(!pd->buf)
    // 	goto quit;
    init_completion(&pd->thread_run); // 通知内核线程退出
    init_completion(&pd->dma_finish);
    pd->thread = kthread_run(thread_hsadc, pd, "mxs-hsadcd");

    rlevel++;

    if (hsadc_init_cdev(pd))
        goto quit;

#if HSADC_DEBUG
    printk(KERN_INFO "%s> probe successed.\n", HSADC_DEVICE_NAME);
#endif

    return 0;

quit:
    pr_err("%s quit at rlevel %d\n", __func__, rlevel);
    switch (rlevel)
    {
    case 14:
        hsadc_cleanup_cdev(pd);
    case 13:
        if (pd->buf_phy)
            dma_free_coherent(NULL, DMA_BUF_SIZE, pd->buf, pd->buf_phy);
    case 12:
        if (pd->desc)
            mxs_dma_free_desc(pd->desc);
    case 11:
        mxs_dma_release(pd->dma_ch, pd->dev);
    case 10:
        free_irq(pd->dev_irq, pd);
    case 9:
        free_irq(pd->dma_irq, pd);
    case 8:
        clk_disable(pd->pwm_clk);
        clk_disable(pd->hsadc_clk);
        clk_disable(pd->ref_hsadc_clk);
    case 7:
        clk_put(pd->pwm_clk);
    case 6:
        clk_put(pd->hsadc_clk);
    case 5:
        clk_put(pd->ref_hsadc_clk);
    case 4:
        iounmap(pd->pwm_base);
    case 3:
        iounmap(pd->hsadc_base);
    case 2:
        platform_set_drvdata(pdev, NULL);
    case 1:
        kfree(pd);
    case 0:
    default:
        return -ENODEV;
    }
}

static int __devexit mxs_hsadc_remove(struct platform_device *pdev)
{
    struct mxs_hsadc_data *pd = platform_get_drvdata(pdev);
    u32 ctrl = 0;

    hsadc_cleanup_cdev(pd); // clean cdev interface

    if (pd->thread)
    {
        kthread_stop(pd->thread);
    }

    if (pd->buf_phy)
        dma_free_coherent(NULL, DMA_BUF_SIZE, pd->buf, pd->buf_phy);

    if (pd->desc)
        mxs_dma_free_desc(pd->desc);

    mxs_dma_enable_irq(pd->dma_ch, 0);
    ctrl = readl(pd->hsadc_base + HSADC_CTRL1);
    ctrl &= ~(1 << 31 | 1 << 30 | 1 << 29); // disable irq
    writel(ctrl, pd->hsadc_base + HSADC_CTRL1);
    mxs_dma_disable(pd->dma_ch);

    mxs_dma_release(pd->dma_ch, pd->dev);
    free_irq(pd->dev_irq, pd);
    free_irq(pd->dma_irq, pd);

    clk_disable(pd->hsadc_clk);
    clk_disable(pd->pwm_clk);
    clk_disable(pd->ref_hsadc_clk);

    clk_put(pd->hsadc_clk);
    clk_put(pd->pwm_clk);
    clk_put(pd->ref_hsadc_clk);

    iounmap(pd->pwm_base);
    iounmap(pd->hsadc_base);

    platform_set_drvdata(pdev, NULL);
    kfree(pd);

#if HSADC_DEBUG
    printk(KERN_INFO "%s> driver removed.\n", HSADC_DEVICE_NAME);
#endif

    return 0;
}

#ifdef CONFIG_PM
static int
mxs_hsadc_suspend(struct platform_device *pdev, pm_message_t state)
{
    return 0;
}

static int mxs_hsadc_resume(struct platform_device *pdev)
{
    return 0;
}
#else
#define mxs_hsadc_suspend NULL
#define mxs_hsadc_resume NULL
#endif

static struct platform_driver mxs_hsadc_driver = {
    .probe = mxs_hsadc_probe,
    .remove = __exit_p(mxs_hsadc_remove),
    .suspend = mxs_hsadc_suspend,
    .resume = mxs_hsadc_resume,
    .driver = {
        .name = "mxs-hsadc",
        .owner = THIS_MODULE,
    },
};

static int __init mx28_hsadc_init(void)
{
    return platform_driver_register(&mxs_hsadc_driver);
}

static void __exit mx28_hsadc_exit(void)
{
    platform_driver_unregister(&mxs_hsadc_driver);
}

MODULE_DESCRIPTION("i.MX28 HSADC driver");
MODULE_LICENSE("GPL");
MODULE_VERSION("1.0.9");

module_init(mx28_hsadc_init);
module_exit(mx28_hsadc_exit);
